import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { ElMessage } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
import { useTokenExpiredStore } from '@/store/modules/tokenExpired';
import {
  isTokenInvalidCode,
  isSuccessCode,
  getResultCodeMessage
} from '@/constants/common/result-code';
import { isUndefined } from 'lodash-es';
import { decryptData, encryptData } from '@/lib/encrypt';
import { DATA_TYPE_ENUM } from '@/constants/common/data-type';

// 创建axios实例 进行基本参数配置
const service = axios.create({
  // 默认请求地址，根据环境的不同可在.env 文件中进行修改
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 设置接口访问超时时间
  timeout: 3000000, // request timeout，
  // 跨域时候允许携带凭证
  withCredentials: true
});

//  request interceptor 接口请求拦截
service.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    /**
     * 用户登录之后获取服务端返回的token,后面每次请求都在请求头中带上token进行JWT校验
     * token 存储在本地储存中（storage）、pinia
     */
    const userStore = useUserStore();
    const token: string = userStore.token;
    // 自定义请求头
    if (token) {
      config.headers['Authorization'] = token;
    }
    return config;
  },
  (error: AxiosError) =>
    // 请求错误，这里可以用全局提示框进行提示
    Promise.reject(error)
);

// 移除旧的防抖弹窗逻辑，使用新的token过期管理器
//  response interceptor 接口响应拦截
// 添加刷新token的标记
let isRefreshing = false;
// 存储等待中的请求
let subscribers: Function[] = [];

// 添加刷新token的方法
const refreshToken = async () => {
  const userStore = useUserStore();
  try {
    const newToken = await userStore.refreshUserToken();
    return newToken;
  } catch (error) {
    userStore.resetToken();
    throw error;
  }
};

// 修改响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    // 如果是加密数据
    if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
      response.data.encryptData = response.data.data;
      const decryptStr = decryptData(response.data.data);
      if (decryptStr) {
        response.data.data = JSON.parse(decryptStr);
      }
    }

    const { code, msg } = response.data;
    if (isSuccessCode(code)) {
      return response.data;
    }
    // 响应数据为二进制流处理(Excel导出)
    if (response.data instanceof ArrayBuffer) {
      return response;
    }

    ElMessage.error(msg || getResultCodeMessage(code));
    return Promise.reject(new Error(msg || getResultCodeMessage(code)));
  },
  async (error: any) => {
    const originalRequest = error.config;
    const userStore = useUserStore();

    // 如果是token过期错误
    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        // 如果正在刷新token，将请求加入队列
        return new Promise(resolve => {
          subscribers.push(() => {
            originalRequest.headers['Authorization'] = userStore.token;
            resolve(service(originalRequest));
          });
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        const newToken = await refreshToken();
        userStore.token = newToken;

        // 重试原始请求
        originalRequest.headers['Authorization'] = newToken;

        // 执行等待中的请求
        subscribers.forEach(cb => cb());
        subscribers = [];

        return service(originalRequest);
      } catch (error) {
        console.log(error);
        // 使用新的token过期store
        const tokenExpiredStore = useTokenExpiredStore();
        tokenExpiredStore.handleTokenExpired();
        return Promise.reject(error);
      } finally {
        isRefreshing = false;
      }
    }
    if (error.response.data) {
      const { code, msg } = error.response.data;
      // token 过期,重新登录
      if (isTokenInvalidCode(code)) {
        console.log('token 过期,重新登录');
        const tokenExpiredStore = useTokenExpiredStore();
        tokenExpiredStore.handleTokenExpired();
      } else {
        ElMessage.error(msg || '系统出错');
      }
    }
    return Promise.reject(error.message);
  }
);

// ================================= 对外提供请求方法：通用请求，get， post, 下载download等 =================================

/**
 * get请求
 */
export const getRequest = (url: string, params?: any) =>
  request({
    url,
    method: 'get',
    params
  });

/**
 * 通用请求封装
 * @param config 请求配置
 */
export const request = (config: AxiosRequestConfig) => service.request(config);

/**
 * post请求
 */
export const postRequest = (url: string, data?: any) =>
  request({
    data,
    url,
    method: 'post'
  });

// ================================= 加密 =================================

/**
 * 加密请求参数的post请求
 */
export const postEncryptRequest = (url: string, data?: any) =>
  request({
    data: { encryptData: encryptData(data) },
    url,
    method: 'post'
  });

// ================================= 下载 =================================

export const postDownload = function (url: string, data?: any) {
  request({
    method: 'post',
    url,
    data,
    responseType: 'blob'
  })
    .then(data => {
      handleDownloadData(data);
    })
    .catch(error => {
      handleDownloadError(error);
    });
};

/**
 * 文件下载
 */
export const getDownload = function (url: string, params?: any) {
  request({
    method: 'get',
    url,
    params,
    responseType: 'blob'
  })
    .then(data => {
      handleDownloadData(data);
    })
    .catch(error => {
      handleDownloadError(error);
    });
};

function handleDownloadError(error: any) {
  if (error instanceof Blob) {
    const fileReader = new FileReader();
    fileReader.readAsText(error);
    fileReader.onload = () => {
      const msg = fileReader.result;
      const jsonMsg = JSON.parse(msg);
      ElMessage.closeAll();
      ElMessage.error(jsonMsg.msg);
    };
  } else {
    ElMessage.closeAll();
    ElMessage.error('网络发生错误');
  }
}

function handleDownloadData(response: any) {
  if (!response) {
    return;
  }

  // 获取返回类型
  const contentType = isUndefined(response.headers['content-type'])
    ? response.headers['Content-Type']
    : response.headers['content-type'];

  // 构建下载数据
  const url = window.URL.createObjectURL(new Blob([response.data], { type: contentType }));
  const link = document.createElement('a');
  link.style.display = 'none';
  link.href = url;

  // 从消息头获取文件名
  const str = isUndefined(response.headers['content-disposition'])
    ? response.headers['Content-Disposition'].split(';')[1]
    : response.headers['content-disposition'].split(';')[1];

  const filename = isUndefined(str.split('fileName=')[1])
    ? str.split('filename=')[1]
    : str.split('fileName=')[1];
  link.setAttribute('download', decodeURIComponent(filename));

  // 触发点击下载
  document.body.appendChild(link);
  link.click();

  // 下载完释放
  document.body.removeChild(link); // 下载完成移除元素
  window.URL.revokeObjectURL(url); // 释放掉blob对象
}

export default service;
